`timescale 1ns / 1ps
//////////////////////////////////////////////////////////////////////////////////
// Company: CBICR, Tsinghua Univ.
// Engineer: Hongyi Li
// 
// Create Date: 2024/12/30 10:44:16
// Design Name: 
// Module Name: SMART Router
// Project Name: 
// Target Devices: 
// Tool Versions: 
// Description: 
// 
// Dependencies: 
// 
// Revision:
// Revision 0.01 - File Created
// Additional Comments:
// 
//////////////////////////////////////////////////////////////////////////////////

module SmartRouter
#(
    parameter DataWidth = 'd32,
    parameter TokenWidth = 'd1,
    parameter LkAheadWidth = 'd14,
    parameter FifoDepth = 'd4,
    parameter VCNumber  = 'd4,
    parameter CoreID    = 6'd1
)(
    input                                        clk, rst_n,
    // I: Token for Routing
    input   [5                          -1:0]    i_tokens, 
    // I: LookAhead Msg for Flow-Control / Bypass
    input   [LkAheadWidth               -1:0]    i_lookahead_e, i_lookahead_w, i_lookahead_n, 
    input   [LkAheadWidth               -1:0]    i_lookahead_s, i_lookahead_l, 
    // I: Data
    input   [DataWidth                  -1:0]    i_data_e, i_data_w, i_data_n, i_data_s, i_data_l,
    // I: Credit
    input   [VCNumber*$clog2(FifoDepth) -1:0]    i_credit_e, i_credit_w, i_credit_n, i_credit_s, i_credit_l,
    // O: LookAhead Msg for Flow-Control / Bypass
    output  [LkAheadWidth               -1:0]    o_lookahead_e, o_lookahead_w, o_lookahead_n, 
    output  [LkAheadWidth               -1:0]    o_lookahead_s, o_lookahead_l, 
    // O: Data
    output  [DataWidth                  -1:0]    o_data_e, o_data_w, o_data_n, o_data_s, o_data_l,
    // O: Credit
    output  [VCNumber*$clog2(FifoDepth) -1:0]    o_credit_e, o_credit_w, o_credit_n, o_credit_s, o_credit_l
);

wire [DataWidth                    -1:0]      i_data_array      [0:4];
wire [VCNumber                     -1:0]      i_available_vc    [0:4];
wire [5                            -1:0]      i_bypass_lacc;

wire                                          bw_valid          [0:4];
wire [DataWidth                    -1:0]      bw_head           [0:4];
wire [DataWidth                    -1:0]      bw_data           [0:4];
wire [VCNumber * $clog2(FifoDepth) -1:0]      bw_credits        [0:4];
wire [VCNumber                     -1:0]      bw_pop_reqs       [0:4];
wire [VCNumber                     -1:0]      bw_vc_winners     [0:4];

wire [VCNumber                     -1:0]      va_next_vc        [0:4];
wire [6                            -1:0]      nrc_x_nxt_hop     [0:4];
wire [6                            -1:0]      nrc_y_nxt_hop     [0:4];

// Data of Stage 1 (Taking bypass into consideration)
wire [5                            -1:0]      bypass_is_head;
wire [5                            -1:0]      bypass_enabled;

wire [25                           -1:0]      sa_mat;
wire [5                            -1:0]      sa_pop;

wire [DataWidth                    -1:0]      s1_data          [0:4];
wire [DataWidth                    -1:0]      s1_head          [0:4];
wire [5                            -1:0]      s1_req           [0:4];
wire [5                            -1:0]      s1_is_head;
wire [5                            -1:0]      s1_bypass;

reg  [DataWidth                    -1:0]      s2_data          [0:4];
reg  [25                           -1:0]      s2_sa_mat;

assign i_data_array[0] = i_data_e;
assign i_data_array[1] = i_data_w;
assign i_data_array[2] = i_data_n;
assign i_data_array[3] = i_data_s;
assign i_data_array[4] = i_data_l;

assign o_credit_e = bw_credits[0];
assign o_credit_w = bw_credits[1];
assign o_credit_n = bw_credits[2];
assign o_credit_s = bw_credits[3];
assign o_credit_l = bw_credits[4];

// i: direction-ewnsl
// j: virtual-channel
genvar i, j, k; 
generate
    for (j = 0; j < VCNumber; j = j + 1) begin
        assign i_available_vc[0][j] = ~(|i_credit_e[$clog2(FifoDepth)*(j+1)-1:$clog2(FifoDepth)*j]);
        assign i_available_vc[1][j] = ~(|i_credit_w[$clog2(FifoDepth)*(j+1)-1:$clog2(FifoDepth)*j]);
        assign i_available_vc[2][j] = ~(|i_credit_n[$clog2(FifoDepth)*(j+1)-1:$clog2(FifoDepth)*j]);
        assign i_available_vc[3][j] = ~(|i_credit_s[$clog2(FifoDepth)*(j+1)-1:$clog2(FifoDepth)*j]);
        assign i_available_vc[4][j] = ~(|i_credit_l[$clog2(FifoDepth)*(j+1)-1:$clog2(FifoDepth)*j]);
    end
endgenerate

// ------------------------------- Stage 1 -------------------------------

// Buffer Write & Switch Allocation-I & Virtual Channel Allocation

generate
    // Five Directions
    for (i = 0; i < 5; i = i + 1) begin

        // Bypass MUX
        assign bypass_is_head[i] = (i_data_array[i][3:0] == 4'b0) && (i_data_array[i][VCNumber+19:20] != 0);
        assign bypass_enabled[i] = (~bw_valid[i]) & (i_bypass_lacc[i]);

        // BW: Buffer Write (EWNSL) + mSA-I
        BufferWrArb #(
            .DataWidth(DataWidth), .FifoDepth(FifoDepth), 
            .VCNumber(VCNumber)
        ) BW_U (
            // Input
            .clk           ( clk               ), 
            .rst_n         ( rst_n             ), 
            .i_data        ( i_data_array[i]   ), 
            .i_pop_reqs    ( bw_pop_reqs[i]    ), 
            .i_bypass      ( bypass_enabled[i] ),
            // Output (No VC)
            .o_valid       ( bw_valid[i]       ), 
            .o_credits     ( bw_credits[i]     ), 
            .o_head        ( bw_head[i]        ), 
            .o_data        ( bw_data[i]        ),
            .o_winner      ( bw_vc_winners[i]  )
        );

        BypassMux #(
            .DataWidth(DataWidth),
            .VCNumber(VCNumber)
        ) ByMUX_U (
            .clk              ( clk               ), 
            .rst_n            ( rst_n             ),
            .i_bypass_is_head ( bypass_is_head[i] ),
            .i_bypass_enabled ( bypass_enabled[i] ),
            .i_bypass_data    ( i_data_array[i]   ),
            .i_bw_data        ( bw_data[i]        ),
            .i_bw_head        ( bw_head[i]        ),
            .s1_data          ( s1_data[i]        ),
            .s1_head          ( s1_head[i]        ),
            .s1_bypass        ( s1_bypass[i]      ),
            .s1_req           ( s1_req[i]         )
        );
    end
endgenerate

// VA: Virtual-Channel Allocation
VcAllocNby #( 
    .VCNumber  ( VCNumber )
) VA_U (
    .clk       ( clk ), 
    .rst_n     ( rst_n ), 
    .i_credit  ( {i_available_vc[4], i_available_vc[3], i_available_vc[2], 
                  i_available_vc[1], i_available_vc[0]} ),
    .o_next_vc ( {    va_next_vc[4],     va_next_vc[3],     va_next_vc[2],
                      va_next_vc[1],     va_next_vc[0]} )
);

// LA-CC
LkAheadConflict #(
    .DataWidth(DataWidth),
    .LkAheadWidth('d14)
) LA_CC_U (
    .clk           ( clk           ),
    .rst_n         ( rst_n         ),
    .i_lookahead_e ( i_lookahead_e ),
    .i_lookahead_w ( i_lookahead_w ),
    .i_lookahead_n ( i_lookahead_n ),
    .i_lookahead_s ( i_lookahead_s ),
    .i_lookahead_l ( i_lookahead_l ),
    .o_bypass      ( i_bypass_lacc )
);

// ------------------------------- Stage 2 -------------------------------

// Buffer Read & Switch Allocation-II

generate
    // RC in Pure Logic Circuits
    for (i = 0; i < 5; i = i + 1) begin
        LkAheadRtrComp LA_RC_U (
            .clk         ( clk                ),
            .rst_n       ( rst_n              ),
            .i_token     ( i_tokens           ),
            .i_x_hop     ( s1_data[i][13:8]   ),
            .i_y_hop     ( s1_data[i][19:14]  ),
            .o_x_nxt_hop ( nrc_x_nxt_hop[i]   ),
            .o_y_nxt_hop ( nrc_y_nxt_hop[i]   )
        );
    end
endgenerate

// mSA-II
SwAlloc SA_U (
    .clk          ( clk ), 
    .rst_n        ( rst_n ),
    .i_adj_mat    ( {s1_req[4], s1_req[3], s1_req[2], s1_req[1], s1_req[0]} ),
    .o_alloc_mat  ( sa_mat )
);

// Buffer Read: Dedicated Switch + Winner VC
assign sa_pop[0] = sa_mat[0] | sa_mat[5] | sa_mat[10] | sa_mat[15] | sa_mat[20];
assign sa_pop[1] = sa_mat[1] | sa_mat[6] | sa_mat[11] | sa_mat[16] | sa_mat[21];
assign sa_pop[2] = sa_mat[2] | sa_mat[7] | sa_mat[12] | sa_mat[17] | sa_mat[22];
assign sa_pop[3] = sa_mat[3] | sa_mat[8] | sa_mat[13] | sa_mat[18] | sa_mat[23];
assign sa_pop[4] = sa_mat[4] | sa_mat[9] | sa_mat[14] | sa_mat[19] | sa_mat[24];

assign bw_pop_reqs[0] = {VCNumber{sa_pop[0] & (~s1_bypass[0])}} & bw_vc_winners[0];
assign bw_pop_reqs[1] = {VCNumber{sa_pop[1] & (~s1_bypass[1])}} & bw_vc_winners[1];
assign bw_pop_reqs[2] = {VCNumber{sa_pop[2] & (~s1_bypass[2])}} & bw_vc_winners[2];
assign bw_pop_reqs[3] = {VCNumber{sa_pop[3] & (~s1_bypass[3])}} & bw_vc_winners[3];
assign bw_pop_reqs[4] = {VCNumber{sa_pop[4] & (~s1_bypass[4])}} & bw_vc_winners[4];

assign s1_is_head[0] = (s1_data[0][3:0] == 4'b0) && (s1_data[0][VCNumber+19:20] != 0);
assign s1_is_head[1] = (s1_data[1][3:0] == 4'b0) && (s1_data[1][VCNumber+19:20] != 0);
assign s1_is_head[2] = (s1_data[2][3:0] == 4'b0) && (s1_data[2][VCNumber+19:20] != 0);
assign s1_is_head[3] = (s1_data[3][3:0] == 4'b0) && (s1_data[3][VCNumber+19:20] != 0);
assign s1_is_head[4] = (s1_data[4][3:0] == 4'b0) && (s1_data[4][VCNumber+19:20] != 0);

// Staged Regs with VC & X/Y hop update
always @(posedge clk) begin
    if (~rst_n) begin
        s2_sa_mat  <= 0;
        s2_data[0] <= 0;
        s2_data[1] <= 0;
        s2_data[2] <= 0;
        s2_data[3] <= 0;
        s2_data[4] <= 0;
    end else begin
        s2_sa_mat  <= sa_mat;         //            8-bit                     VC(4)-bit             6bit              6bit           8bit
        s2_data[0] <= s1_is_head[0] ? {s1_data[0][DataWidth-1:VCNumber+20], va_next_vc[0], nrc_y_nxt_hop[0], nrc_x_nxt_hop[0], s1_data[0][7:0]} 
                                    :  s1_data[0];
        s2_data[1] <= s1_is_head[1] ? {s1_data[1][DataWidth-1:VCNumber+20], va_next_vc[1], nrc_y_nxt_hop[1], nrc_x_nxt_hop[1], s1_data[1][7:0]} 
                                    :  s1_data[1];
        s2_data[2] <= s1_is_head[2] ? {s1_data[2][DataWidth-1:VCNumber+20], va_next_vc[2], nrc_y_nxt_hop[2], nrc_x_nxt_hop[2], s1_data[2][7:0]} 
                                    :  s1_data[2];
        s2_data[3] <= s1_is_head[3] ? {s1_data[3][DataWidth-1:VCNumber+20], va_next_vc[3], nrc_y_nxt_hop[3], nrc_x_nxt_hop[3], s1_data[3][7:0]} 
                                    :  s1_data[3];
        s2_data[4] <= s1_is_head[4] ? {s1_data[4][DataWidth-1:VCNumber+20], va_next_vc[4], nrc_y_nxt_hop[4], nrc_x_nxt_hop[4], s1_data[4][7:0]} 
                                    :  s1_data[4];
    end
end

// LA-Gen
LkAheadGen # (
    .DataWidth     ( DataWidth     ),
    .LkAheadWidth  ( LkAheadWidth  )
) LA_GEN_U (
    .clk           ( clk           ), 
    .rst_n         ( rst_n         ),
    .i_head_e      ( s1_head[0]    ),
    .i_head_w      ( s1_head[1]    ),
    .i_head_n      ( s1_head[2]    ),
    .i_head_s      ( s1_head[3]    ),
    .i_head_l      ( s1_head[4]    ),
    .i_alloc_mat   ( sa_mat        ),
    .o_lookahead_e ( o_lookahead_e ),
    .o_lookahead_w ( o_lookahead_w ),
    .o_lookahead_n ( o_lookahead_n ),
    .o_lookahead_s ( o_lookahead_s ),
    .o_lookahead_l ( o_lookahead_l )
);

// ------------------------------- Stage 3 -------------------------------

// ST: Switch Traveral (P x P)
SmartSwTrav # (
    .DataWidth    ( DataWidth ),
    .VCNumber     ( VCNumber )
) ST_U (
    .clk          ( clk ), 
    .rst_n        ( rst_n ),
    .i_alloc_mat  ( s2_sa_mat ),
    .i_data_e     ( s2_data[0] ), 
    .i_data_w     ( s2_data[1] ), 
    .i_data_n     ( s2_data[2] ), 
    .i_data_s     ( s2_data[3] ), 
    .i_data_l     ( s2_data[4] ),
    .o_data_e     ( o_data_e ),
    .o_data_w     ( o_data_w ),
    .o_data_n     ( o_data_n ),
    .o_data_s     ( o_data_s ),
    .o_data_l     ( o_data_l )
);

endmodule
